#define __USE_XOPEN
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <time.h>
#include <string.h>

#define BUFFER_SIZE 50
#define PATH "./savedata.dbs"

typedef struct tm stime;
typedef struct scheduler
{
    char event[BUFFER_SIZE];
    time_t start;
    time_t end; 
} scheduler;

stime InitStime();
int InputOption();
void InputString(char str[BUFFER_SIZE], char* prompt);
void InputTime(stime* pstm, char* prompt);
void InputDate(stime* psstm, stime* pestm, char* prompt);
//void DisplayStime(stime stm);
void DisplayScheduler(scheduler s);
void CheckAndSortScheduler(scheduler* ps, int cnt);
scheduler* DeleteScheduler(int i, scheduler *ps, int *pcnt);
scheduler* LoadNeedFree(char* path, int *pcnt);
void Save(char* path, scheduler* ps, int cnt);

int main()
{
    int op, cnt = 0;
    scheduler *ps = LoadNeedFree(PATH, &cnt);
 
    stime sstm;
    stime estm;
    stime *psstm;
    stime *pestm;

    time_t cur_time;

    do
    {
        sstm = InitStime();
        estm = InitStime();
        psstm = &sstm;
        pestm = &estm;

        op = InputOption();
        switch(op)
        {
            case 1:
                if(cnt)
                    ps = (scheduler*)realloc(ps, sizeof(scheduler)*(cnt+1));
                InputString(ps[cnt].event, "What is the event (DO NOT USE '@'!):");
                InputDate(psstm, pestm, "Event date:");
                do
                {
                    InputTime(psstm, "Start time:");
                    InputTime(pestm, "End time:");
                    ps[cnt].start = mktime(psstm);
                    ps[cnt].end = mktime(pestm);
                    if(ps[cnt].end - ps[cnt].start <= 0)
                        printf("The start time is later than the end time, please check the input!\n");
                }while(ps[cnt].end - ps[cnt].start <= 0);
                cnt++;
                CheckAndSortScheduler(ps, cnt);
                break;
            case 2:
            {
                bool is_display = false;
                for(int i=0;i<cnt;i++)
                {
                    if(!is_display)
                    {
                        printf("Schedule:\n");
                        is_display = true; 
                    }
                    DisplayScheduler(ps[i]);
                }
                if(!is_display)
                    printf("There are currently no events.\n");
                break;
            }
            case 3:
            {
                bool is_active = false;
                int j = -1;
                cur_time = time(NULL);
                for(int i=0;i<cnt;i++)
                {
                    if(cur_time>=ps[i].start&&cur_time<=ps[i].end)
                    {
                        if(!is_active)
                            printf("Currently active events:\n");
                        is_active = true;
                        DisplayScheduler(ps[i]);
                    }
                    if(cur_time<ps[i].start)j=i;
                }
                if(!is_active)
                {
                    printf("There are currently no active events.\n");
                    if(j!=-1)
                    {
                        printf("You can prepare for the next event:\n");
                        DisplayScheduler(ps[j]);
                    }
                    else
                    {
                        printf("Congratulations, you've done everything!\n");
                    }
                }
                break;
            }
            case 4:
            {
                bool is_delete = false;
                cur_time = time(NULL);
                for(int i=0;i<cnt;i++)
                {
                    if(cur_time > ps[i].end)
                    {
                        if(!is_delete)
                            printf("Deleting:\n");
                        is_delete = true;
                        DisplayScheduler(ps[i]);
                        ps = DeleteScheduler(i, ps, &cnt);
                        i--;
                    }
                }
                if(!is_delete)
                    printf("No expired events.\n");
                break;
            }
            default:
                break;
        }
    }while(op);

    Save(PATH, ps, cnt);

    if(ps)
        free(ps);

    printf("Thank you for using the scheduler.\n");

    return 0;
}

int InputOption()
{

    char buffer[10];
    int op = -2;
    do
    {
        printf("1 - Insert a new event\n" \
               "2 - Display all events\n" \
               "3 - Now?\n" \
               "4 - Delete expired\n" \
               "0 - Exit\n" \
               "Please select an option: ");

        fgets(buffer, sizeof(buffer), stdin);

        if(feof(stdin)) return 0; 

        if(strlen(buffer)>0)
            buffer[strlen(buffer)-1]='\0';
        sscanf(buffer, "%d", &op);

    }while(op < 0 || op > 4);
    return op;
}

void InputString(char str[BUFFER_SIZE], char* prompt)
{
    char buffer[100], *p;
    do
    {
        printf("%s ", prompt);
        fgets(buffer, sizeof(buffer), stdin);
        if(strlen(buffer)>0)
            buffer[strlen(buffer)-1] = '\0';
        p = buffer;
        while(*p == ' ')p++;
    }while(strlen(p)<=0);
    strncpy(str, p, BUFFER_SIZE);
    str[BUFFER_SIZE-1] = '\0';
}
    
void InputTime(stime* pstm, char* prompt)
{
    char buffer[BUFFER_SIZE];
    int result;
    do
    {
        InputString(buffer, prompt);
        result = strptime(buffer, "%I:%M%p", pstm);
    }while(!result);
}

void InputDate(stime* psstm, stime* pestm, char* prompt)
{
    char buffer[BUFFER_SIZE];
    int result;
    do
    {
        InputString(buffer, prompt);
        result = strptime(buffer, "%m/%d/%Y", psstm);
    }while(!result);
    (*pestm).tm_mday = (*psstm).tm_mday;
    (*pestm).tm_mon = (*psstm).tm_mon;
    (*pestm).tm_year = (*psstm).tm_year;
    (*pestm).tm_wday = (*psstm).tm_wday;
    (*pestm).tm_yday = (*psstm).tm_yday;
}

//void DisplayStime(stime stm)
//{
//   int *p = &stm;
//   for(int i=0;i<9;i++)
//   {
//       printf("%d ",*(p++));
//   }
//   printf("\n");
//}

stime InitStime()
{
    stime stm;
    stm.tm_sec = 0;
    stm.tm_min = 0;
    stm.tm_hour = 0;
    stm.tm_mday = 1;
    stm.tm_mon = 0;
    stm.tm_year = 0; stm.tm_wday = 0;
    stm.tm_yday = 0;
    stm.tm_isdst = -1;

    return stm;
}

void CheckAndSortScheduler(scheduler* ps, int cnt)
{
    bool overlap = false, is_switch = false;
    scheduler s = ps[cnt-1];
    for(int i=0;i<cnt;i++)
    {
        if(i!=cnt-1||is_switch)
        {
            if(s.start>=ps[i].start && s.start<ps[i].end || s.end>ps[i].start && s.end<=ps[i].end)
            {
                if(!overlap)
                    printf("Warning, this event overlaps:\n");
                DisplayScheduler(ps[i]);
                overlap = true;
            }
        }
        if(!is_switch && s.start<ps[i].start)
        {
            for(int j=cnt-1;j>i;j--)
            {
                ps[j] = ps[j-1];
            }
            ps[i] = s;
            is_switch = true;
            i++;
        }
    }
}

void DisplayScheduler(scheduler s)
{
    char sbuffer[BUFFER_SIZE], ebuffer[BUFFER_SIZE];
    stime sstm = *localtime(&s.start), estm = *localtime(&s.end);
    strftime(sbuffer, sizeof(sbuffer), "%m/%d/%Y  %H:%M%p", &sstm);
    strftime(ebuffer, sizeof(ebuffer), "%H:%M%p", &estm);
    printf(" %s  %s  %s\n", sbuffer, ebuffer, s.event);
}

scheduler* DeleteScheduler(int i, scheduler *ps, int *pcnt)
{
    for(int j=i+1;j<*pcnt;j++)
    {
        ps[j-1] = ps[j];
    }
    (*pcnt)--;
    if(*pcnt==0)return ps;
    ps = (scheduler*)realloc(ps, sizeof(scheduler)*(*pcnt));
    return ps;
}

scheduler* LoadNeedFree(char* path, int *pcnt)
{
    char buffer[3*(BUFFER_SIZE)], event[BUFFER_SIZE];
    time_t start = 0, end = 0;
    scheduler *ps = (scheduler*)malloc(sizeof(scheduler));
    FILE *pf = fopen(path, "r");
    if(pf==NULL)
    {
        *pcnt = 0;
    }
    else
    {
        while(!feof(pf))
        {
            buffer[0] = '\0';
            fgets(buffer, sizeof(buffer), pf);
            if(feof(pf))break;
            if(strlen(buffer)>0)
            {
                buffer[strlen(buffer)-1] = '\0';
                sscanf(buffer, "%[^@]@ %ld @ %ld", event, &start, &end);
                event[sizeof(event)-1] = '\0';
		if(strlen(event)==0||start==0||end==0)
                {
                    event[0] = '\0';
                    start = 0;
                    end = 0;
                    continue;
                }
                if(*pcnt)
                    ps = (scheduler*)realloc(ps, sizeof(scheduler)*((*pcnt)+1));
                strncpy(ps[*pcnt].event, event, BUFFER_SIZE);
                ps[*pcnt].event[BUFFER_SIZE-1] = '\0';
                ps[*pcnt].start = start;
                ps[*pcnt].end = end;
                event[0] = '\0';
                start = 0;
                end = 0;
                (*pcnt)++;
            }
        }
    }
    if(pf)
        fclose(pf);
    return ps;
}

void Save(char* path, scheduler* ps, int cnt)
{
    FILE* pf = fopen(path, "w");
    if(pf == NULL)
    {
        printf("There may be an error in saving the path. This operation was not saved!\n");
    }
    else
    {
        for(int i=0;i<cnt;i++)
        {
            fprintf(pf, "%s@ %ld @ %ld\n", ps[i].event, ps[i].start, ps[i].end);
        }
    } 
    if(pf)
        fclose(pf);
}
